package com.chaofan.authsso.client.filter;

import cn.hutool.json.JSONUtil;
import com.chaofan.authsso.client.common.ApplyTokenResult;
import com.chaofan.authsso.client.common.Constant;
import com.chaofan.authsso.client.common.SsoRestfulMessage;
import com.chaofan.authsso.client.properties.SsoRegisterProperties;
import com.chaofan.authsso.client.util.LogMsgUtil;
import com.chaofan.authsso.client.util.SsoUrlUtil;
import com.chaofan.authsso.client.util.TokenUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;

/**
 * 这个过滤器用于拦截所有的接口请求，从session中取token并验证
 * Author: Chaofan
 * Date: 2020/6/4 14:24
 * email: chaofan2685@qq.com
 */
public class LoginCheckFilter implements Filter {

    public static final Logger logger = LoggerFactory.getLogger(LoginCheckFilter.class);

    private SsoRegisterProperties properties;

    public LoginCheckFilter(SsoRegisterProperties properties) {
        this.properties = properties;
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {}

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        SsoRestfulMessage<String> message = null;
        logger.info(LogMsgUtil.buildAuthSsoLog(Constant.AUTH_SSO_FILTER_BEGIN_STR));
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse res = (HttpServletResponse) servletResponse;
        String path = req.getRequestURI().substring(req.getContextPath().length()).replaceAll("[/]+$", "");
        logger.info(LogMsgUtil.buildAuthSsoLog(path));
        //判断请求是否在白名单中
        if (checkWhitelists(path)){
            //如果在，直接放过不作处理
            logger.info(LogMsgUtil.buildAuthSsoLog(Constant.URL_IN_WHITELISTS_STR));
            filterChain.doFilter(servletRequest, servletResponse);
            return;
        }else {
            try {
                //如果不在，获取token并校验
                HttpSession session = req.getSession();
                Object token = session.getAttribute(properties.getTokenName());
                //判断session中是否存在token
                if (token == null){
                    //不存在则申请token并存入session,之后返回未登录状态
                    message = applyToken(session);
                }else {
                    //存在则校验token
                    String authSsoToken = token.toString();
                    logger.info(LogMsgUtil.buildAuthSsoLog(Constant.TOKEN_NAME_STR+authSsoToken));
                    SsoRestfulMessage<ApplyTokenResult> restfulMessage = TokenUtil.checkToken(properties.getHost(),authSsoToken);
                    if (restfulMessage.successful()){
                        //返回8200说明校验成功,不作处理
                        logger.info(LogMsgUtil.buildAuthSsoLog(Constant.CHECK_TOKEN_OK_STR));
                        filterChain.doFilter(servletRequest, servletResponse);
                        return;
                    }else if (restfulMessage.getCode().intValue() == Constant.CREATE_NEW_TOKEN.intValue()){
                        //返回8301则说明校验不通过,需要重新申请token
                        logger.info(LogMsgUtil.buildAuthSsoLog(Constant.CHECK_TOKEN_FAILURE_STR));
                        message = applyToken(session);
                    }else if (restfulMessage.getCode().intValue() == Constant.APPLY_TOKEN.intValue()){
                        //返回8304则说明需要用返回的token替换当前token
                        ApplyTokenResult applyTokenResult = restfulMessage.getData();
                        logger.info(LogMsgUtil.buildAuthSsoLog(Constant.AUTH_SSO_TOKEN_APPLY_STR+applyTokenResult.getToken()));
                        authSsoToken = applyTokenResult.getToken();
                        //首先校验新的token是否可用
                        restfulMessage = TokenUtil.checkToken(properties.getHost(),authSsoToken);
                        if (restfulMessage.successful()){
                            //然后将其存入session
                            session.setAttribute(properties.getTokenName(),authSsoToken);
                            filterChain.doFilter(servletRequest, servletResponse);
                            return;
                        }else {
                            message = buildFailRest(restfulMessage.getMessage());
                        }
                    }else {
                        message = buildFailRest(restfulMessage.getMessage());
                    }
                }
            }catch (Exception e){
                message = buildFailRest(Constant.AUTH_SSO_FILTER_ERROR_HEADER+e.getMessage());
                e.printStackTrace();
            }
        }
        res.setHeader(Constant.CONTENT_TYPE_KEY,Constant.CONTENT_TYPE_VALUE);
        ServletOutputStream out = res.getOutputStream();
        out.write(JSONUtil.toJsonStr(message).getBytes());
        out.flush();
    }

    @Override
    public void destroy() {}

    private boolean checkWhitelists(String url){
        for (String white : properties.getWhitelist()){
            if (url.startsWith(white)){
                return true;
            }
        }
        return false;
    }

    private SsoRestfulMessage buildNotLogin(Object data){
        SsoRestfulMessage ssoRestfulMessage = new SsoRestfulMessage();
        ssoRestfulMessage.setCode(Constant.NOT_LOGIN);
        ssoRestfulMessage.setMessage(Constant.NOT_LOGIN_MESSAGE);
        ssoRestfulMessage.setData(data);
        return ssoRestfulMessage;
    }

    private SsoRestfulMessage buildFailRest(String message){
        SsoRestfulMessage ssoRestfulMessage = new SsoRestfulMessage();
        ssoRestfulMessage.setCode(Constant.INTERNAL_SERVER_ERROR);
        ssoRestfulMessage.setMessage(message);
        return ssoRestfulMessage;
    }

    /**
     * 申请token
     * @param session
     * @return
     */
    private SsoRestfulMessage<String> applyToken(HttpSession session){
        SsoRestfulMessage<String> message;
        SsoRestfulMessage<ApplyTokenResult> applyRestful = TokenUtil.applyToken(properties.getHost(),session);
        if (applyRestful.successful()){
            ApplyTokenResult applyTokenResult = applyRestful.getData();
            session.setAttribute(properties.getTokenName(),applyTokenResult.getToken());
            message = buildNotLogin(SsoUrlUtil.getLoginUrl(properties.getFrontHost(),applyTokenResult.getAuthCode()));
        }else {
            message = buildFailRest(applyRestful.getMessage());
        }
        return message;
    }
}
